// t.d(i, { I6: () => PriorityCriteria, at: () => TilePrioritizer });

import * as THREE from "three"
import PanoQualityManager, { TestResults } from "../utils/PanoQualityManager"
import a from "../math/14844"
import u from "../math/19674"
import PersistentStorage from "../storage/PersistentStorage"
import l from "../math/51263"
import w from "../math/57051"
import ObservableValue, { createObVal } from "../observer/observable.value"
import Vectors from "../three/Vectors"
import browserUtil from "../utils/browser.util"
import g from "../math/82088"
import resolutionEnum, { resolutionVal, resolutionMap } from "../enum/resolution.enum"
import configConst from "../constant/config.const"
import viewmodeEnum, { viewRange } from "../enum/viewmode.enum"
import { tileStatus } from "../enum/tile.enum"
import TilingSettings from "../settings/TilingSettings"
import RaycasterData from "../data/RaycasterData"
import SweepData from "../data/SweepData"
import { RequestQueue } from "../utils/request.util"
import Sweep from "../object/Sweep"
var r = {
  "0": "None",
  "1": "DirectionalFOV",
  None: 0,
  DirectionalFOV: 1
}

class PriorityCriteria {
  hoveredSweep: Sweep
  viewMode: number
  cameraRot: THREE.Quaternion
  sweep: Sweep
  cameraPosition: THREE.Vector3
  cameraDir: THREE.Vector3
  upcomingSweeps: any
  zoomingActive: boolean
  constructor(e, i: THREE.Vector3, t: THREE.Vector3, r?) {
    this.cameraRot = new THREE.Quaternion()
    this.sweep = e
    this.cameraPosition = new THREE.Vector3().copy(i)
    this.cameraDir = new THREE.Vector3().copy(t)
    this.upcomingSweeps = r
    this.zoomingActive = !1
  }
}
const T = (e, i, t = !1) => {
  let r = 0
  if (e && i) for (const o of i) (t && -1 !== e.indexOf(o)) || (e.push(o), r++)
  return r
}
export default class TilePrioritizer {
  panoQualityManager: PanoQualityManager
  tilingSettings: TilingSettings
  sweepData: SweepData
  raycaster: RaycasterData
  tempQueue: RequestQueue[]
  filterAndPrioritize: any
  priorityCriteria: PriorityCriteria
  currViewQueue: Record<string, ObservableValue<number>>
  fullPanoQueue: Record<string, ObservableValue<number>>
  queueRaycast: any
  queueRaycastIntersection: any
  getSweepIdInLocalDirection: any
  persistentStorage: PersistentStorage
  maxResolution: number
  isMobileDevice: boolean
  static DIRECTIONAL_FOV: number
  constructor(e: PanoQualityManager, i: TilingSettings, t, r) {
    this.panoQualityManager = e
    this.tilingSettings = i
    this.sweepData = t
    this.raycaster = r
    this.tempQueue = []
    this.filterAndPrioritize = (e, i, t) => {
      if (!this.priorityCriteria.sweep) return
      const r = void 0 !== this.priorityCriteria.upcomingSweeps && null !== this.priorityCriteria.upcomingSweeps
      const o = this.tempQueue
      o.length = 0
      this.queueTilesForPano(o, t, this.priorityCriteria.sweep, resolutionMap.BASE)
      const s = T(e, o, !0)
      this.currViewQueue[resolutionEnum.BASE].value = s
      this.fullPanoQueue[resolutionEnum.BASE].value = s
      r
        ? this.queueForRestrictedSweeps(e, t)
        : this.priorityCriteria.viewMode !== viewmodeEnum.Panorama
        ? this.queueForNonPanoViewmode(e, t)
        : this.queueForPanoViewmode(e, i, t)
      this.tilingSettings.downloadFullPano && this.queueFullPano(e, t)
    }
    this.queueRaycast = (() => {
      const e = new THREE.Vector3()
      return (i, t) => {
        if (this.priorityCriteria.sweep)
          if (this.priorityCriteria.hoveredSweep) this.queueTilesForPano(i, t, this.priorityCriteria.hoveredSweep, resolutionMap.BASE)
          else if (this.raycaster && this.raycaster.hit) {
            const r = e.copy(this.raycaster.hit.point).sub(this.priorityCriteria.sweep.position),
              o = this.getSinglePanoInDirection(r),
              s = this.sweepData.getSweep(o)
            s && this.queueTilesForPano(i, t, s, resolutionMap.BASE)
          }
      }
    })()
    this.queueRaycastIntersection = (e, i) => {
      if (!this.raycaster || !this.raycaster.hit) return
      const t = g.bG(this.sweepData, !1, this.raycaster.hit.intersection),
        r = t.length > 0 ? t[0].sweep : this.sweepData.getClosestSweep(this.raycaster.hit.point, !0)
      r && this.queueTilesForPano(e, i, r, resolutionMap.BASE)
    }
    this.getSweepIdInLocalDirection = (() => {
      const e = new THREE.Vector3()
      return i => {
        const t = this.priorityCriteria.cameraRot,
          r = e.copy(i).applyQuaternion(t)
        return this.getSinglePanoInDirection(r)
      }
    })()
    this.persistentStorage = new PersistentStorage()
    this.maxResolution = e.getNavPanoSize()
    this.currViewQueue = F()
    this.fullPanoQueue = F()
    this.isMobileDevice = browserUtil.isMobile()
    this.priorityCriteria = new PriorityCriteria(null, new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, 0, -1))
  }
  getQualityQueueSize(e, i) {
    return e === viewRange.CurrentView ? this.currViewQueue[i].value : this.fullPanoQueue[i].value
  }
  makeQueueSubscription(e, i, t) {
    return (e === viewRange.CurrentView ? this.currViewQueue[i] : this.fullPanoQueue[i]).onChanged(t)
  }
  updateCriteria(e: Sweep, i, t, r) {
    this.priorityCriteria.sweep = e
    this.priorityCriteria.cameraPosition.copy(i)
    this.priorityCriteria.cameraDir.copy(t)
    this.priorityCriteria.cameraRot.copy(r)
  }
  setHoveredSweep(e: Sweep) {
    this.priorityCriteria.hoveredSweep = e
  }
  setUpcomingSweeps(e) {
    this.priorityCriteria.upcomingSweeps = e
  }
  clearUpcomingSweeps() {
    this.priorityCriteria.upcomingSweeps = void 0
  }
  setDownloadFOV(e: number) {
    TilePrioritizer.DIRECTIONAL_FOV = e
  }
  queueForRestrictedSweeps(e, i) {
    if (!this.priorityCriteria.upcomingSweeps) return
    let t = 0
    for (const r of this.priorityCriteria.upcomingSweeps) if ((t++, this.queueTilesForPano(e, i, r, resolutionMap.BASE), t >= 3)) break
    this.queueFOVStandardNarrow(e, i), this.queueFOVHighNarrow(e, i)
  }
  queueForPanoViewmode(e, i, t) {
    this.queueRaycast(e, t),
      this.queueFOVStandardNarrow(e, t),
      this.queueScoredSweeps(e, i, t),
      this.queueFOVHighNarrow(e, t),
      this.isMobileDevice || this.queueWASD(e, i, t)
  }
  queueForNonPanoViewmode(e, i) {
    this.queueRaycastIntersection(e, i)
  }
  queueFOVTiles(e, i, t, r) {
    if (!this.priorityCriteria.sweep) return 0
    const o = resolutionVal[i]
    return this.canDownloadSize(this.priorityCriteria.sweep, i)
      ? this.queueTilesInDirectionForPano(e, r, this.priorityCriteria.sweep, o, this.priorityCriteria.cameraDir, t)
      : 0
  }
  queueScoredSweeps(e, i, t) {
    if (this.priorityCriteria.sweep && this.maxResolution <= 2048) {
      const r = this.persistentStorage.getArray("filterAndPrioritize:scoredSweeps")
      this.populateScoredSweeps(this.priorityCriteria.sweep, i, r, this.priorityCriteria.cameraDir, 6), this.queueTilesForPanos(e, r, t, resolutionMap.BASE, 4)
    }
  }
  queueFOVStandardNarrow(e, i) {
    if (!this.priorityCriteria.sweep) return
    const t = this.tempQueue
    t.length = 0
    const r = this.queueFOVTiles(t, resolutionEnum.STANDARD, TilePrioritizer.DIRECTIONAL_FOV, i)
    TilePrioritizer.sortTiles(t, this.priorityCriteria.sweep, this.priorityCriteria.cameraDir)
    T(e, t)
    this.currViewQueue[resolutionEnum.STANDARD].value = r
    this.fullPanoQueue[resolutionEnum.STANDARD].value = r
  }
  queueFOVHighNarrow(e, i) {
    if (!this.priorityCriteria.sweep) return
    const t = this.tempQueue
    t.length = 0
    const r = this.queueFOVTiles(t, resolutionEnum.HIGH, TilePrioritizer.DIRECTIONAL_FOV, i)
    const o = this.queueFOVTiles(t, resolutionEnum.ULTRAHIGH, TilePrioritizer.DIRECTIONAL_FOV, i)
    TilePrioritizer.sortTiles(t, this.priorityCriteria.sweep, this.priorityCriteria.cameraDir)
    T(e, t)
    this.currViewQueue[resolutionEnum.HIGH].value = r
    this.currViewQueue[resolutionEnum.ULTRAHIGH].value = o
  }
  queueFullPano(e, i) {
    if (!this.priorityCriteria.sweep) return
    const t = this.tempQueue
    t.length = 0
    if (this.maxResolution <= resolutionMap.HIGH) {
      if (this.canDownloadSize(this.priorityCriteria.sweep, resolutionEnum.HIGH)) {
        const e = this.queueTilesForPano(t, i, this.priorityCriteria.sweep, resolutionMap.HIGH)
        this.fullPanoQueue[resolutionEnum.HIGH].value = e
      }
    } else if (this.canDownloadSize(this.priorityCriteria.sweep, resolutionEnum.ULTRAHIGH)) {
      const e = this.queueTilesForPano(t, i, this.priorityCriteria.sweep, resolutionMap.ULTRAHIGH)
      this.fullPanoQueue[resolutionEnum.ULTRAHIGH].value = e
    }
    TilePrioritizer.sortTiles(t, this.priorityCriteria.sweep, this.priorityCriteria.cameraDir)
    T(e, t, !0)
  }
  queueWASD(e, i, t) {
    const r = this.persistentStorage.getArray("filterAndPrioritize:neighbors") || []
    if (((r.length = 0), !this.priorityCriteria.sweep)) return
    const o = [Vectors.FORWARD, Vectors.RIGHT, Vectors.LEFT, Vectors.BACK]
    for (const e of o) {
      const t = this.getSweepIdInLocalDirection(e),
        o = i.get(t)
      o && r.push(o)
    }
    this.queueTilesForPanos(e, r, t, resolutionMap.BASE)
  }
  canDownloadSize(e, i) {
    const t = resolutionVal[i],
      r = this.panoQualityManager.getNavPanoSize() >= t || (this.maxResolution >= t && this.panoQualityManager.getZoomPanoSize() >= t),
      o = this.panoQualityManager.getTestResults(e.id, i)
    return r && o === TestResults.Untested && this.panoQualityManager.testDownload(e, i, a.I_), o === TestResults.Success && r
  }
  populateScoredSweeps(e, i, t, r, s) {
    ;(t = t || []).length = 0
    const a = new THREE.Vector3().copy(e.position),
      n = [l._k(), l.ff(e), l.jN(a, 400), l.pI(a, r, 0.75)],
      u = [w.Dv(a, configConst.tileConfig.navigation.distanceFactor), w.o7(a, r, configConst.tileConfig.navigation.directionFactor)],
      p = this.sweepData.getSweepNeighbours(e),
      c = this.sweepData.sortByScore(n, u, p)
    for (let e = 0; e < c.length && e < s; e++) {
      const i = c[e].sweep
      t.push(i)
    }
  }
  queueTilesForPanos(e, i, t, r, o?) {
    let s = 0
    for (const a of i) {
      if (((s += this.queueTilesForPano(e, t, a, r) > 0 ? 1 : 0), o && s >= o)) break
    }
    return s
  }
  queueTilesForPano(e, i, t: Sweep, o) {
    const s = this.persistentStorage.get("queueTilesForSweep:filterCriteria", () => ({ filter: r.None }))
    return this.filterAndQueueTileDownloadDescriptors(e, i, t, o, s)
  }
  queueTilesInDirectionForPano(e, i, t, s, n, u) {
    const p = this.persistentStorage.get("queueTilesInDirectionForSweep:panoSpaceDir", () => new THREE.Vector3()),
      h = this.persistentStorage.get("queueTilesInDirectionForSweep:filterCriteria", () => ({
        filter: r.DirectionalFOV,
        direction: new THREE.Vector3(),
        fov: 60
      }))
    return p.copy(n), a.ym(t.rotation, p), h.direction.copy(p), (h.fov = u), this.filterAndQueueTileDownloadDescriptors(e, i, t, s, h)
  }
  filterAndQueueTileDownloadDescriptors(e, i, t: Sweep, r, o) {
    const s = this.persistentStorage.getArray("filterAndQueueTileDownloadDescriptors:descriptors"),
      a = i.getTileDownloadDescriptors(t, r)
    ;(s.length = 0), this.filterTileDownloadDescriptors(a, s, o)
    let n = 0
    for (const i of s) i && (e.push(i), n++)
    return n
  }
  filterTileDownloadDescriptors(e, i, t) {
    let o, s
    switch (t.filter) {
      case r.DirectionalFOV:
        for (o = 0; o < e.length; o++) (s = e[o]), s && a.eO(s.panoSize, s.tileSize, s.face, s.tileX, s.tileY, t.direction, t.fov) && i.push(s)
        break
      default:
        for (o = 0; o < e.length; o++) (s = e[o]), i.push(s)
    }
    for (o = 0; o < i.length; o++) (s = i[o]), s && !this.canIncludeDescriptor(s) && (i[o] = null)
  }
  canIncludeDescriptor(e) {
    return e.status !== tileStatus.Downloading && e.status !== tileStatus.Downloaded
  }
  static sortTiles(e, i: Sweep, t: THREE.Vector3) {
    C.panoSpaceDir.copy(t)
    a.ym(i.rotation, C.panoSpaceDir)
    C.fovThresholdNarrow = u.tf(TilePrioritizer.DIRECTIONAL_FOV)
    e.sort(A)
  }
  static insertSortedPanoTile(e, i, t, r: THREE.Vector3) {
    C.panoSpaceDir.copy(r)
    a.ym(t.rotation, C.panoSpaceDir)
    C.fovThresholdNarrow = u.tf(TilePrioritizer.DIRECTIONAL_FOV)
    let o = -1
    for (let t = 0; t < e.length; t++) {
      if (A(i, e[t]) <= 0) {
        o = t
        break
      }
    }
    if (-1 === o) e[e.length] = i
    else {
      for (let i = e.length; i > o; i--) e[i] = e[i - 1]
      e[o] = i
    }
  }
  getSinglePanoInDirection(e: THREE.Vector3) {
    const i = this.priorityCriteria.sweep
    if (!i) return ""
    const t = [l.ff(i), l._k(), l.pI(i.position, e)]
    const r = [w.o7(i.position, e), w.TE(i.position)]
    const o = i.neighbours
        .filter(e => {
          const i = this.sweepData.getSweep(e)
          return t.every(e => e(i))
        })
        .map(e => {
          const i = this.sweepData.getSweep(e)
          return { sweepId: e, score: r.reduce((e, t) => e + t(i), 0) }
        }),
      s = o.reduce((e, i) => (e.score > i.score ? e : i), o[0])
    return s ? s.sweepId : ""
  }
}
TilePrioritizer.DIRECTIONAL_FOV = 120
const C = { panoSpaceDir: new THREE.Vector3(), fovThresholdNarrow: -1 }
const A = (e, i) => {
  const t = C.panoSpaceDir,
    r = C.fovThresholdNarrow,
    o = Math.max(Math.min(t.dot(e.direction), 1), -1),
    s = Math.max(Math.min(t.dot(i.direction), 1), -1)
  return o >= r && s < r ? -1 : (o < r && s >= r) || e.panoSize > i.panoSize ? 1 : i.panoSize > e.panoSize ? -1 : -(o - s)
}
function F() {
  return {
    [resolutionEnum.BASE]: createObVal(0),
    [resolutionEnum.STANDARD]: createObVal(0),
    [resolutionEnum.HIGH]: createObVal(0),
    [resolutionEnum.ULTRAHIGH]: createObVal(0)
  }
}
